var container, stats;
var camera, scene, renderer;
var splineHelperObjects = [];
var splinePointsLength = 4;
var positions = [];
var point = new THREE.Vector3();
var geometry = new THREE.BoxBufferGeometry(20, 20, 20);
var transformControl;
var ARC_SEGMENTS = 200;
var splines = {};

var params = {
    uniform: true,
    tension: 0.5,
    centripetal: true,
    chordal: true,
    addPoint: addPoint,
    removePoint: removePoint,
    exportSpline: exportSpline
};

function SplineEditor(container) {
    container = document.getElementById('container');
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf0f0f0);
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.set(0, 250, 1000);
    scene.add(camera);
    scene.add(new THREE.AmbientLight(0xf0f0f0));
    var light = new THREE.SpotLight(0xffffff, 1.5);
    light.position.set(0, 1500, 200);
    light.castShadow = true;
    light.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(70, 1, 200, 2000));
    light.shadow.bias = - 0.000222;
    light.shadow.mapSize.width = 1024;
    light.shadow.mapSize.height = 1024;
    scene.add(light);
    var planeGeometry = new THREE.PlaneBufferGeometry(2000, 2000);
    planeGeometry.rotateX(- Math.PI / 2);
    var planeMaterial = new THREE.ShadowMaterial({ opacity: 0.2 });
    var plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.position.y = - 200;
    plane.receiveShadow = true;
    scene.add(plane);
    var helper = new THREE.GridHelper(2000, 100);
    helper.position.y = - 199;
    helper.material.opacity = 0.25;
    helper.material.transparent = true;
    scene.add(helper);
    //var axes = new THREE.AxesHelper( 1000 );
    //axes.position.set( - 500, - 500, - 500 );
    //scene.add( axes );
    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);
    stats = new Stats();
    container.appendChild(stats.dom);
    var gui = new dat.GUI();
    gui.add(params, 'uniform');
    gui.add(params, 'tension', 0, 1).step(0.01).onChange(function (value) {
        splines.uniform.tension = value;
        updateSplineOutline();
    });
    gui.add(params, 'centripetal');
    gui.add(params, 'chordal');
    gui.add(params, 'addPoint');
    gui.add(params, 'removePoint');
    gui.add(params, 'exportSpline');
    gui.open();
    // Controls
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    controls.damping = 0.2;
    controls.addEventListener('change', render);
    controls.addEventListener('start', function () {
        cancelHideTransorm();
    });
    controls.addEventListener('end', function () {
        delayHideTransform();
    });
    transformControl = new THREE.TransformControls(camera, renderer.domElement);
    transformControl.addEventListener('change', render);
    transformControl.addEventListener('dragging-changed', function (event) {
        controls.enabled = !event.value;
    });
    scene.add(transformControl);
    // Hiding transform situation is a little in a mess :()
    transformControl.addEventListener('change', function () {
        cancelHideTransorm();
    });
    transformControl.addEventListener('mouseDown', function () {
        cancelHideTransorm();
    });
    transformControl.addEventListener('mouseUp', function () {
        delayHideTransform();
    });
    transformControl.addEventListener('objectChange', function () {
        updateSplineOutline();
    });
    var dragcontrols = new THREE.DragControls(splineHelperObjects, camera, renderer.domElement); //
    dragcontrols.enabled = false;
    dragcontrols.addEventListener('hoveron', function (event) {
        transformControl.attach(event.object);
        cancelHideTransorm();
    });
    dragcontrols.addEventListener('hoveroff', function () {
        delayHideTransform();
    });
    var hiding;
    function delayHideTransform() {
        cancelHideTransorm();
        hideTransform();
    }
    function hideTransform() {
        hiding = setTimeout(function () {
            transformControl.detach(transformControl.object);
        }, 2500);
    }
    function cancelHideTransorm() {
        if (hiding) clearTimeout(hiding);
    }
    /*******
     * Curves
     *********/
    for (var i = 0; i < splinePointsLength; i++) {
        addSplineObject(positions[i]);
    }
    positions = [];
    for (var i = 0; i < splinePointsLength; i++) {
        positions.push(splineHelperObjects[i].position);
    }
    var geometry = new THREE.BufferGeometry();
    geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(ARC_SEGMENTS * 3), 3));
    var curve = new THREE.CatmullRomCurve3(positions);
    curve.curveType = 'catmullrom';
    curve.mesh = new THREE.Line(geometry.clone(), new THREE.LineBasicMaterial({
        color: 0xff0000,
        opacity: 0.35
    }));
    curve.mesh.castShadow = true;
    splines.uniform = curve;
    curve = new THREE.CatmullRomCurve3(positions);
    curve.curveType = 'centripetal';
    curve.mesh = new THREE.Line(geometry.clone(), new THREE.LineBasicMaterial({
        color: 0x00ff00,
        opacity: 0.35
    }));
    curve.mesh.castShadow = true;
    splines.centripetal = curve;
    curve = new THREE.CatmullRomCurve3(positions);
    curve.curveType = 'chordal';
    curve.mesh = new THREE.Line(geometry.clone(), new THREE.LineBasicMaterial({
        color: 0x0000ff,
        opacity: 0.35
    }));
    curve.mesh.castShadow = true;
    splines.chordal = curve;
    for (var k in splines) {
        var spline = splines[k];
        scene.add(spline.mesh);
    }
    load([new THREE.Vector3(289.76843686945404, 452.51481137238443, 56.10018915737797),
    new THREE.Vector3(- 53.56300074753207, 171.49711742836848, - 14.495472686253045),
    new THREE.Vector3(- 91.40118730204415, 176.4306956436485, - 6.958271935582161),
    new THREE.Vector3(- 383.785318791128, 491.1365363371675, 47.869296953772746)]);
}

SplineEditor.prototype.addSplineObject = function (position) {
    var material = new THREE.MeshLambertMaterial({ color: Math.random() * 0xffffff });
    var object = new THREE.Mesh(geometry, material);
    if (position) {
        object.position.copy(position);
    } else {
        object.position.x = Math.random() * 1000 - 500;
        object.position.y = Math.random() * 600;
        object.position.z = Math.random() * 800 - 400;
    }
    object.castShadow = true;
    object.receiveShadow = true;
    scene.add(object);
    splineHelperObjects.push(object);
    return object;
};

SplineEditor.prototype.addPoint = function () {
    splinePointsLength++;
    positions.push(addSplineObject().position);
    updateSplineOutline();
};

SplineEditor.prototype.removePoint = function () {
    if (splinePointsLength <= 4) {
        return;
    }
    splinePointsLength--;
    positions.pop();
    scene.remove(splineHelperObjects.pop());
    updateSplineOutline();
};

SplineEditor.prototype.updateSplineOutline = function () {
    for (var k in splines) {
        var spline = splines[k];
        var splineMesh = spline.mesh;
        var position = splineMesh.geometry.attributes.position;
        for (var i = 0; i < ARC_SEGMENTS; i++) {
            var t = i / (ARC_SEGMENTS - 1);
            spline.getPoint(t, point);
            position.setXYZ(i, point.x, point.y, point.z);
        }
        position.needsUpdate = true;
    }
};

SplineEditor.prototype.exportSpline = function () {
    var strplace = [];
    for (var i = 0; i < splinePointsLength; i++) {
        var p = splineHelperObjects[i].position;
        strplace.push('new THREE.Vector3({0}, {1}, {2})'.format(p.x, p.y, p.z));
    }
    console.log(strplace.join(',\n'));
    var code = '[' + (strplace.join(',\n\t')) + ']';
    prompt('copy and paste code', code);
}

SplineEditor.prototype.load = function (new_positions) {
    while (new_positions.length > positions.length) {
        addPoint();
    }
    while (new_positions.length < positions.length) {
        removePoint();
    }
    for (var i = 0; i < positions.length; i++) {
        positions[i].copy(new_positions[i]);
    }
    updateSplineOutline();
};

SplineEditor.prototype.animate = function () {
    requestAnimationFrame(animate);
    render();
    stats.update();
};

SplineEditor.prototype.render = function () {
    splines.uniform.mesh.visible = params.uniform;
    splines.centripetal.mesh.visible = params.centripetal;
    splines.chordal.mesh.visible = params.chordal;
    renderer.render(scene, camera);
};

export default SplineEditor;